package com.autumn.mybatis.criterion;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.autumn.mybatis.criterion.expressions.ColumnExpression;
import com.autumn.mybatis.criterion.expressions.AbstractCriteriaExpression;
import com.autumn.mybatis.criterion.expressions.AbstractExpression;
import com.autumn.mybatis.metadata.EntityTable;
import com.autumn.util.ExceptionUtils;
import com.autumn.util.StringUtils;
import com.autumn.util.tuple.TupleTwo;

/**
 * 条件 Bean 解析
 * 
 * @author 老码农
 *
 *         2017-10-29 16:01:22
 */
public class CriteriaBeanParser<E extends ICriteriaBean> extends AbstractParser {

	private final List<E> criteriaBeans;
	private static Map<String, CriteriaOperatorEnum> CRITERIAOPERATOR_MAP = new HashMap<>();
	private static Map<String, LogicalOperatorEnum> LOGICALOPERATOR_MAP = new HashMap<>();

	static {
		CRITERIAOPERATOR_MAP.put("=", CriteriaOperatorEnum.EQ);
		CRITERIAOPERATOR_MAP.put("EQ", CriteriaOperatorEnum.EQ);
		CRITERIAOPERATOR_MAP.put("<>", CriteriaOperatorEnum.NOT_EQ);
		CRITERIAOPERATOR_MAP.put("NOT_EQ", CriteriaOperatorEnum.NOT_EQ);
		CRITERIAOPERATOR_MAP.put("NOTEQ", CriteriaOperatorEnum.NOT_EQ);
		CRITERIAOPERATOR_MAP.put("!=", CriteriaOperatorEnum.NOT_EQ);
		CRITERIAOPERATOR_MAP.put(">", CriteriaOperatorEnum.GT);
		CRITERIAOPERATOR_MAP.put("GT", CriteriaOperatorEnum.GT);
		CRITERIAOPERATOR_MAP.put(">=", CriteriaOperatorEnum.GE);
		CRITERIAOPERATOR_MAP.put("GE", CriteriaOperatorEnum.GE);
		CRITERIAOPERATOR_MAP.put("<", CriteriaOperatorEnum.LT);
		CRITERIAOPERATOR_MAP.put("LT", CriteriaOperatorEnum.LT);
		CRITERIAOPERATOR_MAP.put("<=", CriteriaOperatorEnum.LE);
		CRITERIAOPERATOR_MAP.put("LE", CriteriaOperatorEnum.LE);
		CRITERIAOPERATOR_MAP.put("LIKE", CriteriaOperatorEnum.LIKE);
		CRITERIAOPERATOR_MAP.put("LEFT_LIKE", CriteriaOperatorEnum.LEFT_LIKE);
		CRITERIAOPERATOR_MAP.put("LEFTLIKE", CriteriaOperatorEnum.LEFT_LIKE);
		CRITERIAOPERATOR_MAP.put("LLIKE", CriteriaOperatorEnum.LEFT_LIKE);
		CRITERIAOPERATOR_MAP.put("RIGHT_LIKE", CriteriaOperatorEnum.RIGHT_LIKE);
		CRITERIAOPERATOR_MAP.put("RIGHTLIKE", CriteriaOperatorEnum.RIGHT_LIKE);
		CRITERIAOPERATOR_MAP.put("RLIKE", CriteriaOperatorEnum.RIGHT_LIKE);
		CRITERIAOPERATOR_MAP.put("BETWEEN", CriteriaOperatorEnum.BETWEEN);
		CRITERIAOPERATOR_MAP.put("B", CriteriaOperatorEnum.BETWEEN);

		LOGICALOPERATOR_MAP.put("AND", LogicalOperatorEnum.AND);
		LOGICALOPERATOR_MAP.put("OR", LogicalOperatorEnum.OR);
		LOGICALOPERATOR_MAP.put("NOT", LogicalOperatorEnum.NOT);
	}

	/**
	 * 实例化 CriteriaBeanParser
	 * 
	 * @param entityTable
	 *            表
	 * @param criteriaBeans
	 *            条件集合
	 */
	public CriteriaBeanParser(EntityTable entityTable, List<E> criteriaBeans) {
		super(entityTable);
		this.criteriaBeans = criteriaBeans;
	}

	/**
	 * 解析
	 * 
	 * @return 返回 条件表达式
	 */
	public AbstractCriteriaExpression parser() {
		if (this.criteriaBeans == null || this.criteriaBeans.size() == 0) {
			return null;
		}
		AbstractCriteriaExpression expression = null;
		for (E item : criteriaBeans) {
			if (item == null) {
				ExceptionUtils.throwArgumentException("item", "条件项目存在为 null 的 Bean。");
			}
			TupleTwo<LogicalOperatorEnum, AbstractCriteriaExpression> logicExp = createCriteriaExpression(item);
			if (expression == null) {
				expression = logicExp.getItem2();
			} else {
				expression = AbstractExpression.logical(logicExp.getItem1(), expression, logicExp.getItem2());
			}
		}
		return expression;
	}

	/**
	 * 
	 * @param item
	 * @return
	 */
	private TupleTwo<LogicalOperatorEnum, AbstractCriteriaExpression> createCriteriaExpression(E item) {
		TupleTwo<LogicalOperatorEnum, CriteriaOperatorEnum> tuple = checkItem(item);
		ColumnExpression col = column(item.getExpression());
		AbstractCriteriaExpression criteriaExp;
		if (tuple.getItem2().equals(CriteriaOperatorEnum.BETWEEN)) {
			criteriaExp = AbstractExpression.binary(CriteriaOperatorEnum.BETWEEN, col, AbstractExpression
					.between(constantValue(col, item.getValue()), constantValue(col, item.getSecondValue())));
		} else {
			criteriaExp = AbstractExpression.binary(tuple.getItem2(), col, constant(col, item.getValue()));
		}
		return new TupleTwo<LogicalOperatorEnum, AbstractCriteriaExpression>(tuple.getItem1(), criteriaExp);
	}

	/**
	 * 
	 * @param item
	 * @return
	 */
	private TupleTwo<LogicalOperatorEnum, CriteriaOperatorEnum> checkItem(E item) {
		if (StringUtils.isNullOrBlank(item.getLogic())) {
			item.setLogic("AND");
		}
		if (StringUtils.isNullOrBlank(item.getExpression())) {
			ExceptionUtils.throwArgumentException("item", "条件项目的表达式 expression 为 null 或空白值。");
		}
		if (StringUtils.isNullOrBlank(item.getOp())) {
			ExceptionUtils.throwArgumentException("item", "条件项目的运算符 op 为 null 或空白值。");
		}
		if (item.getValue() == null || StringUtils.isNullOrBlank(item.getValue().toString())) {
			ExceptionUtils.throwArgumentException("item", "条件项目的值 value 为 null。");
		}
		CriteriaOperatorEnum op = CRITERIAOPERATOR_MAP.get(item.getOp().toUpperCase());
		if (op == null) {
			ExceptionUtils.throwArgumentException("item", "条件项目的运算符 op 为[" + item.getOp() + "]无效或不支持。");
		}
		boolean isNotSecondValue = item.getSecondValue() == null
				|| StringUtils.isNullOrBlank(item.getSecondValue().toString());
		if (op.equals(CriteriaOperatorEnum.BETWEEN) && isNotSecondValue) {
			ExceptionUtils.throwArgumentException("item", "条件项目的值 secondValue 为 null。");
		}
		LogicalOperatorEnum logic = LOGICALOPERATOR_MAP.get(item.getLogic().toUpperCase());
		if (logic == null) {
			ExceptionUtils.throwArgumentException("item", "条件项目的逻辑表达式 logic 为[" + item.getLogic() + "]无效或不支持。");
		}
		return new TupleTwo<LogicalOperatorEnum, CriteriaOperatorEnum>(logic, op);
	}

}
